package triIndex;

import java.util.concurrent.locks.*;

import transaction.*;
import triCompact.CompMapMgr;
import triDataStruct.*;
import triIndex.btree.BTree;
import utils.*;

/**
 * TagIndex does not support delete tag
 *
 */
public class Str2IntIndex {
	
	private IMapManager mapMgr;
	
	private HashFunction hash;
	
	private BTree tree;
	
	private ReadWriteLock rwlock;
	private Lock rlock;
	private Lock wlock;
	
	private int indexBufferSize;
	private String filename;
	private int pageBufferSize;
	private int pageSize; 
	private String inodeFilename;
	private int inodePageBufferSize;
	private int inodePageSize;
	
	public Str2IntIndex(HashFunction hash, 
			int indexBufferSize, String filename, int pageBufferSize, int pageSize, 
			String inodeFilename, int inodePageBufferSize, int inodePageSize,
			String btreeFilename, int btreePageBufferSize, int btreePageSize, int btreeOrder) {
		
		this.indexBufferSize = indexBufferSize;
		this.filename = filename;
		this.pageBufferSize = pageBufferSize;
		this.pageSize = pageSize;
		this.inodeFilename = inodeFilename;
		this.inodePageBufferSize = inodePageBufferSize;
		this.inodePageSize = inodePageSize;
		
		this.hash = hash;
		this.mapMgr = new MapManager(indexBufferSize, filename, pageBufferSize, pageSize,
				inodeFilename, inodePageBufferSize, inodePageSize);
		this.tree = new BTree(btreeFilename, btreePageBufferSize, btreePageSize, btreeOrder);
		
		rwlock = new ReentrantReadWriteLock();
		rlock = rwlock.readLock();
		wlock = rwlock.writeLock();
	}
	
	public void init(Transaction tx) {
		mapMgr.init(tx);
		tree.init(tx);
	}
	
	public void compact(String tfilename, String tinode_filename, TxManager txMgr) {
		FileOp.deleteFile(tfilename);
		FileOp.deleteFile(tinode_filename);
		
		wlock.lock();

		CompMapMgr compMapMgr = new CompMapMgr(mapMgr,
				this.indexBufferSize, tfilename, pageBufferSize, pageSize,
				tinode_filename, inodePageBufferSize, inodePageSize);
		mapMgr = compMapMgr;
		
		Transaction tx = txMgr.newTransaction();
		compMapMgr.init(tx);
		tx.commit();
		
		wlock.unlock();
		
		compMapMgr.compact(txMgr);
		
		wlock.lock();
		
		
		tx = txMgr.newTransaction();
		compMapMgr.dispose(tx);
		tx.commit();
		
		FileOp.renameFile(tfilename, this.filename);
		FileOp.renameFile(tinode_filename, this.inodeFilename);
		
		mapMgr = new MapManager(indexBufferSize, filename, pageBufferSize, pageSize,
				inodeFilename, inodePageBufferSize, inodePageSize);
		
		tx = txMgr.newTransaction();
		mapMgr.init(tx);
		tx.commit();
		
		wlock.unlock();
		
	}
	
	public void insert(String key, int value, Transaction tx) {
		int inodePageId = getInodePageId(key, true, tx);
		rlock.lock();
		mapMgr.insert(inodePageId, key, value, tx);
		rlock.unlock();
	}
	
	public CondQueryIntSet find(String key, Transaction tx) {
		int inodePageId = getInodePageId(key, false, tx);
		System.out.println("find " + inodePageId);
		
		if (inodePageId < 0)
			return null;
		
		CondQueryIntSet value;
		rlock.lock();
		value = mapMgr.find(inodePageId, key, tx);
		rlock.unlock();
		return value;
	}
	
	public void remove(String key, int value, Transaction tx) {
		int inodePageId = getInodePageId(key, false, tx);
		if (inodePageId < 0)
			return;
		
		rlock.lock();
		mapMgr.remove(inodePageId, key, value, tx);
		rlock.unlock();
	}
	
	private int getInodePageId(String key, boolean create, Transaction tx) {
		int inodePageId;
		synchronized (hash) {
			int result = hash.compute(key);
			inodePageId = tree.find(result, tx);
			if (inodePageId == BTree.ERROR_CODE && create) {
				inodePageId = mapMgr.createNewMap(); 
				tree.insert(result, inodePageId, tx);
			}
		}
		return inodePageId;
	}
	
	public void dispose(Transaction tx) {
		tree.dispose(tx);
		mapMgr.dispose(tx);
	}

}
